package filescript;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;
import actions.*;
import filters.*;

public class FilterAndAction {
	
	
	private static final String COLON = ":";
	private static final String NOT_OPERATOR = "NOT";
	private static final String AND_OPERATOR = "AND";
	private static final String OR_OPERATOR = "OR";
	
	private ArrayList<String> _filters;
	private String _action;
	private ArrayList<String> _comments;
	private TreeSet<File> _result;
	private TreeSet<File> _globalSet;
	private Action _actionToPerform;
	
	protected FilterAndAction(TreeSet<File> globalSet) {
		_filters = new ArrayList<String>();
		_result = new TreeSet<File>();
		_globalSet = new TreeSet<File>(globalSet);
		_comments = new ArrayList<String>();
	}
	
	protected void addFilter(String filterString) {
		if(filterString!=null) {
			_filters.add(filterString);
		}
	}
	
	protected void setAction(String filterString) {
		if(filterString!=null) {
			_action = new String(filterString);
		}
	}
	
	protected void addComment(String filterString) {
		if(filterString!=null) {
				_comments.add(filterString);
		}
	}
	
	/**
	 * Public static class to perform logic operations on Tree Sets objects
	 * @author avihaya
	 *
	 */
	public static class setLogic {
		/**
		 * 
		 * @param setA
		 * @param setB
		 * @return
		 */
		public static TreeSet<File> LogicalAND(TreeSet<File> setA,TreeSet<File> setB) {
			TreeSet<File> _tmpSet = new TreeSet<File>(setA);
			_tmpSet.retainAll(setB);
			return _tmpSet;
		}
		
		/**
		 * 
		 * @param setA
		 * @param setB
		 * @return
		 */
		public static TreeSet<File> LogicalOR(TreeSet<File> setA,TreeSet<File> setB) {
			TreeSet<File> _tmpSet = new TreeSet<File>(setA);
			_tmpSet.addAll(setB);
			return _tmpSet;
		}
		
		/**
		 * 
		 * @param globalSet
		 * @param set
		 * @return
		 */
		public static TreeSet<File> LogicalNOT(TreeSet<File> globalSet,TreeSet<File> set) {
			TreeSet<File> _tmpSet = new TreeSet<File>(globalSet);
			for(Iterator<File> it1=set.iterator();it1.hasNext();) {
				_tmpSet.remove(it1.next());
			}
			return _tmpSet;
		}
	}
	
	protected void parseFilters() throws InvalidFilterExpression, UnsupportedFilterException, InvalidFilterParameterException {
		FilterManager filterManager = new FilterManager();
		/**
		 * Iterate filters and validate the Expression Tree built from the filter string
		 */
		for(String filter : _filters) {		
			ExpressionTreeNode treeHead = FilterParser.parseExpression(filter); //Build the expression tree
			this.validateExpressionTree(treeHead);
			this.validateFilters(treeHead,filterManager);
			// Execute the filters after we have validated them and maintain the result
			_result.addAll(executeFilters(treeHead,_globalSet));
			//We remove from the global set the result as if a file passed a filter string it will be returned
			_globalSet.removeAll(_result);
		}
	}
	
	protected void verifySectionBuild() throws InvalidSectionException {
		if (_action==null) {
			throw new InvalidSectionException();
		}
	}
	
	protected void validateExpressionTree(ExpressionTreeNode tree) throws InvalidFilterExpression {
		ExpressionTreeNode leftNode = (ExpressionTreeNode)tree.getLeftNode();
		ExpressionTreeNode rightNode = (ExpressionTreeNode)tree.getRightNode();
		Object item = tree.getItem(); // Contain the item as an Object.
		
		if(tree.isLeaf()) {
			return; // Its a leaf. We will not do anything
		}

		if(!validateLogicalOperator((String)item))
			{
				throw new InvalidFilterExpression(); // We are excpecting a valid logical operator
			}
		if(leftNode!=null) {
			Object leftItem = leftNode.getItem();
			if(leftItem instanceof ExpressionTreeNode) {
				validateExpressionTree(rightNode);
			}
		}
		if(rightNode!=null) {
			Object rightItem = rightNode.getItem();
			if(rightItem instanceof ExpressionTreeNode) {
				validateExpressionTree(rightNode);
			}
		}
	}
	
	
	protected boolean validateLogicalOperator(String operatorToValidate) throws InvalidFilterExpression {
		if(operatorToValidate.compareTo(NOT_OPERATOR)!=0 && operatorToValidate.compareTo(AND_OPERATOR)!=0 && operatorToValidate.compareTo(OR_OPERATOR)!=0) {
			throw new InvalidFilterExpression();
		}
		return true;
	}
	
	
	protected void validateFilters(ExpressionTreeNode tree,FilterManager fm) throws UnsupportedFilterException {
		ExpressionTreeNode leftNode = (ExpressionTreeNode)tree.getLeftNode();
		ExpressionTreeNode rightNode = (ExpressionTreeNode)tree.getRightNode();
		if(tree.isLeaf()) { // We have found a Leaf. We will convert its item to a Filter
			String[] filterAndParameter = ((String)tree.getItem()).split(COLON);
			tree.setItem(fm.getFilterByName(filterAndParameter[0])); //Keep Filter to main item
			tree.setMiscItem(filterAndParameter[1]); //Keep Filter's parameter to misc item
		} else {
			if(leftNode!=null) {
				validateFilters(leftNode,fm);
			}
			if(rightNode!=null) {
				validateFilters(rightNode,fm);
			}
		}
	}
	
	protected TreeSet<File> executeFilters(ExpressionTreeNode tree,TreeSet<File> globalSet) throws InvalidFilterParameterException {
		ExpressionTreeNode leftNode = (ExpressionTreeNode)tree.getLeftNode();
		ExpressionTreeNode rightNode = (ExpressionTreeNode)tree.getRightNode();
		if(tree.isLeaf()) { // We have found a Leaf. We already changed this to be a Filter Object
			return ((Filter)tree.getItem()).filter(globalSet, (String)tree.getMiscItem());
		} else {
			if(leftNode!=null) {
				if( ((String)tree.getItem()).equals(AND_OPERATOR)) {
					return setLogic.LogicalAND(executeFilters(leftNode, globalSet), executeFilters(rightNode, globalSet));
				} else {
					return setLogic.LogicalOR(executeFilters(leftNode, globalSet), executeFilters(rightNode, globalSet));
				}	
			} else {
				return setLogic.LogicalNOT(globalSet, executeFilters(rightNode, globalSet));
			}
		}
	}
	
	protected void validateAction()  throws  InvalidActionParametersException, InvalidActionException {
		ActionManager am = new ActionManager();
		String[] actionAndParameter = _action.split(COLON);
		_actionToPerform = am.getActionByName(actionAndParameter[0]);
		_actionToPerform.validateParameter(actionAndParameter[1]);
	}
	
	protected void printComments() {
		for(String comment : _comments) {
			System.out.println(comment);
		}
	}
	
	protected void executeAction() {
		String[] actionAndParameter = _action.split(COLON);
		_actionToPerform.performAction(_result, actionAndParameter[1]);
	}
}
